#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
 
#define NUM 5
#define MAX_ROUND 10
 
int id[NUM] = {0, 1, 2, 3, 4};
enum {thinking, hungry, eating} state[NUM];
pthread_t tid[NUM];
pthread_mutex_t mutex;
pthread_cond_t condition[5];
 
void test(int i) {
	pthread_mutex_lock(&mutex);
	if((state[(i + 4) % 5] != eating) &&
	(state[i] == hungry) &&
	(state[(i + 1) % 5] != eating)) {
		state[i] = eating;
		pthread_cond_signal(&condition[i]);
	}
	pthread_mutex_unlock(&mutex);
}
 
void pickup_forks(int i) {
	state[i] = hungry;
	test(i);
	pthread_mutex_lock(&mutex);
	if(state[i] != eating) {
		pthread_cond_wait(&condition[i], &mutex);
	}
	pthread_mutex_unlock(&mutex);
}
 
void return_forks(int i) {
	state[i] = thinking;
	test((i + 4) % 5);
	test((i + 1) % 5);
}
 
void *run(void *arg) {
	int id = *(int*)arg;
	double hungry_wait_time = 0.0f;
	time_t start, end, t;
	srand((unsigned)time(&t));
	int i = 0;
 
	for(; i < MAX_ROUND; i++) {
		double wait_time = 1 + 2 * 1.0f / 100 * (rand() % 100);
		printf("Philospher %d is thinking for %.2f seconds.\n", id, wait_time);
		sleep(wait_time);
		time(&start);
		pickup_forks(id);
		time(&end);
		hungry_wait_time = difftime(end, start);
		printf("Philospher %d is hungry for %.2f seconds.\n", id, hungry_wait_time);
		
		wait_time = 1 + 2 * 1.0f / 100 * (rand() % 100);
		printf("Philospher %d is eating for %.2f seconds.\n", id, wait_time);
		sleep(wait_time);
		return_forks(id);
	}
	return NULL;
}
 
void init() {
	int i;
	pthread_mutex_init(&mutex, NULL);
	for(i = 0; i < NUM; i++) {
		state[i] = thinking;
		pthread_cond_init(&condition[i], NULL);
	}
}


int main() {
	init();
	int i = 0;
	for(;i < NUM; i++) {
		pthread_create(&tid[i], NULL, run, &id[i]);
	}
	int j;
	for(j = 0; j < NUM; j++) {
		pthread_join(tid[j], NULL);
	}
	return 0;
}